/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { RouterAnalyzer } from './RouterAnalyzer';
import { ComponentAnalyzer } from './ComponentAnalyzer';
import { IConstantResolver } from './interface/IConstantResolver';
import { ConstantResolver } from './utils/ConstantResolver';
import { AbstractAnnotationAnalyzer } from './interface/AbstractAnnotationAnalyzer';

/**
 * Annotation Analyzer Registry
 *
 * This singleton class manages all annotation analyzers used by the HMRouter
 * plugin. It handles registration and retrieval of both built-in analyzers
 * (RouterAnalyzer, ComponentAnalyzer) and extension-provided analyzers.
 *
 * The registry also provides a shared constant resolver instance for
 * resolving string constants in annotations.
 */
export class AnnotationAnalyzerRegistry {
  /**
   * Singleton instance of the registry
   * @private
   */
  private static instance: AnnotationAnalyzerRegistry;

  /**
   * Set of registered analyzers
   * @private
   */
  private analyzers: Set<AbstractAnnotationAnalyzer> = new Set();

  /**
   * Constant resolver instance for resolving string literals in annotations
   * @private
   */
  private constantResolver: IConstantResolver | null = null;

  /**
   * Private constructor to prevent direct instantiation
   * @private
   */
  private constructor() {}

  /**
   * Get the singleton instance of the registry
   *
   * Creates a new instance if one doesn't exist
   *
   * @returns {AnnotationAnalyzerRegistry} The singleton instance
   */
  static getInstance(): AnnotationAnalyzerRegistry {
    if (!this.instance) {
      this.instance = new AnnotationAnalyzerRegistry();
    }
    return this.instance;
  }

  /**
   * Initialize the registry with required analyzers
   *
   * This method creates a constant resolver for the given module
   * and registers the default analyzers (RouterAnalyzer and ComponentAnalyzer).
   * It should be called once before using the registry.
   *
   * @param {string} modulePath - Path to the module for constant resolution
   */
  initialize(modulePath: string): void {
    // Skip if already initialized
    if (this.constantResolver) {
      return;
    }

    this.constantResolver = new ConstantResolver(modulePath);
    this.registerDefaultAnalyzers();
  }

  /**
   * Register a new analyzer
   *
   * @param {AbstractAnnotationAnalyzer} analyzer - The analyzer to register
   */
  registerAnalyzer(analyzer: AbstractAnnotationAnalyzer): void {
    this.analyzers.add(analyzer);
  }

  /**
   * Get all registered analyzers
   *
   * @returns {AbstractAnnotationAnalyzer[]} Array of all registered analyzers
   */
  getAnalyzers(): AbstractAnnotationAnalyzer[] {
    return Array.from(this.analyzers);
  }

  /**
   * Get the constant resolver instance
   *
   * @returns {IConstantResolver} The constant resolver
   * @throws {Error} If the registry hasn't been initialized
   */
  getConstantResolver(): IConstantResolver {
    if (!this.constantResolver) {
      throw new Error('AnnotationAnalyzerRegistry not initialized. Call initialize() first.');
    }
    return this.constantResolver;
  }

  /**
   * Register the default built-in analyzers
   *
   * This method creates and registers the standard analyzers:
   * - RouterAnalyzer: For @HMRouter annotations
   * - ComponentAnalyzer: For service, interceptor, and lifecycle annotations
   *
   * @private
   * @throws {Error} If the registry hasn't been properly initialized
   */
  private registerDefaultAnalyzers(): void {
    if (!this.constantResolver) {
      throw new Error('AnnotationAnalyzerRegistry not initialized properly.');
    }

    // Register built-in analyzers
    this.registerAnalyzer(new RouterAnalyzer(this.constantResolver));
    this.registerAnalyzer(new ComponentAnalyzer(this.constantResolver));
  }
}
